package com.prs.crm.service.impl;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.courser.ui.PaginationSupport;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;

import com.prs.crm.context.AppContext;
import com.prs.crm.context.Constrains;
import com.prs.crm.dao.BaseDao;
import com.prs.crm.domain.FlowEntity;
import com.prs.crm.domain.flow.WorkFlowHandler;
import com.prs.crm.domain.flow.WorkFlowInstance;
import com.prs.crm.domain.flow.WorkFlowNodeVo;
import com.prs.crm.domain.flow.WorkFlowProcess;
import com.prs.crm.domain.flow.WorkFlowProcessNode;
import com.prs.crm.domain.flow.WorkFlowProcessNodeDetail;
import com.prs.crm.domain.hr.Person;
import com.prs.crm.exception.InValidException;
import com.prs.crm.flow.FlowActionListener;
import com.prs.crm.flow.WorkFlowExecutor;
import com.prs.crm.service.FlowService;
import com.prs.crm.service.flow.WorkFlowInstanceService;
import com.prs.crm.service.flow.WorkFlowProcessService;
import com.prs.crm.service.hr.PersonService;
import com.prs.crm.service.sys.FormService;

public abstract class FlowServiceImpl<T extends FlowEntity, K extends BaseDao<T>>
		extends BaseServiceImpl<T, K> implements FlowService<T>,
		FlowActionListener<T> {

	private static Logger log = Logger.getLogger(FlowServiceImpl.class);

	private FormService formService;

	private WorkFlowInstanceService workflowInstanceService;

	private WorkFlowProcessService workflowProcessService;

	private WorkFlowExecutor workflowExecutor;

	private PersonService personService;
	
	protected Class<T> entityClass;

	private FlowActionListener<T> flowListener;

	public void setFormService(FormService formService) {
		this.formService = formService;
	}

	public FormService getFormService() {
		return formService;
	}

	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}

	public PersonService getPersonService() {
		return personService;
	}

	public WorkFlowProcessService getWorkflowProcessService() {
		return workflowProcessService;
	}

	public void setWorkflowProcessService(
			WorkFlowProcessService workflowProcessService) {
		this.workflowProcessService = workflowProcessService;
	}

	public void setWorkflowExecutor(WorkFlowExecutor workflowExecutor) {
		this.workflowExecutor = workflowExecutor;
	}

	public WorkFlowExecutor getWorkflowExecutor() {
		return workflowExecutor;
	}

	public void setFlowListener(FlowActionListener<T> flowListener) {
		this.flowListener = flowListener;
	}

	public FlowActionListener<T> getFlowListener() {
		return flowListener;
	}

	public void setWorkflowInstanceService(
			WorkFlowInstanceService workflowInstanceService) {
		this.workflowInstanceService = workflowInstanceService;
	}

	public WorkFlowInstanceService getWorkflowInstanceService() {
		return workflowInstanceService;
	}

	@SuppressWarnings("unchecked")
	public Class<T> getFormClass() {
		/*for (int i = 0; i < ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments().length; i++) {
			System.out.println((Class<T>) ((ParameterizedType) getClass()
					.getGenericSuperclass()).getActualTypeArguments()[i]+" asdfasdfasdf"+i);
		}*/
		if (entityClass==null)
			entityClass=(Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		return entityClass;
	}

	public WorkFlowProcess submitFlow(T entity, WorkFlowInstance instance) {
		try {
			if (instance != null) {
				if (entity.getProcess() == null) {
					WorkFlowProcess process = this.getWorkflowExecutor()
							.initialize(
									instance,
									AppContext.getAccountContext()
											.currentUser());
					;
					entity.setProcess(process);
					entity.setFlowstatus(Constrains.WORKFLOWENTITY_STATE_INIT);
					this.getWorkflowProcessService().saveProcessEntity(entity);

				}
				startWorkFlow(entity);
				return entity.getProcess();
			} else {
				entity.setFlowstatus(Constrains.WORKFLOWENTITY_STATE_NOT);
				this.getWorkflowProcessService().saveProcessEntity(entity);
				return null;
			}
		} catch (InValidException e) {
			throw e;
		}
	}

	public void onNextStep(T entity, Collection<WorkFlowProcessNode> nextNodes) {

	}

	public void execute(WorkFlowProcessNode node,
			WorkFlowProcessNodeDetail detail, T entity, Person executor) {
		detail.setExecutor(executor);
		Collection<WorkFlowProcessNode> nextNodes = this.getWorkflowExecutor()
				.execute(node, detail,this);
		if (nextNodes != null)
			this.onNextStep(entity, nextNodes);
	}

	public void setWorkFlowHandler(T entity, List<WorkFlowHandler> handlers) {
		int i = 0;
		for (WorkFlowProcessNode node : entity.getProcess().getProcessNodes()) {
			if (node.getHandler() != null && handlers.size() > i) {
				WorkFlowHandler handler = handlers.get(i++);
				node.getHandler().setDept(handler.getDept());
				node.getHandler().setRole(handler.getRole());
				node.getHandler().getExecutors().clear();
				node.getHandler().getExecutors().addAll(handler.getExecutors());
			} else {
				node.setHandler(handlers.get(i++));
			}

		}
	}

	public void startWorkFlow(T entity) {
		if (entity.getFlowstatus() != null
				&& entity.getFlowstatus() == Constrains.WORKFLOWENTITY_STATE_PROCESS)
			throw new InValidException("已经处于审批状态,不能重复提交");
		Collection<WorkFlowProcessNode> nodes = this.getWorkflowExecutor()
				.start(entity.getProcess());
		entity.setFlowstatus(Constrains.WORKFLOWENTITY_STATE_PROCESS);
		this.getWorkflowProcessService().saveProcessEntity(entity);
		if (nodes != null)
			this.onNextStep(entity, nodes);
	}

	public void cancelProcess(T entity) {
		if (entity.getProcess() != null) {
			if (entity.getFlowstatus() != Constrains.WORKFLOWENTITY_STATE_INIT)
				throw new InValidException(
						"error,not remove the form when the workflow  have started");
			WorkFlowProcess process = entity.getProcess();
			entity.setProcess(null);
			entity.setFlowstatus(Constrains.WORKFLOWENTITY_STATE_NOT);
			this.getWorkflowProcessService().saveProcessEntity(entity);

			this.getWorkflowProcessService().remove(process);
		}
	}

	public boolean valideSubmit(T entity) {
		try {
			if (entity != null
					&& PropertyUtils.getProperty(entity, "id") != null)
				return true;
			else
				return false;
		} catch (Exception e) {
			e.printStackTrace();
			log.error(e);
			return false;
		}
	}

	/**
	 * 查询指定流程状态的实体
	 * 
	 * @param detachCriteria
	 * @param page
	 * @param status
	 * @return
	 */
	public PaginationSupport pageQueryByFlowStatus(
			final DetachedCriteria detachCriteria,
			final PaginationSupport page, Integer[] status) {
		detachCriteria.add(Restrictions.in("flowstatus", status));
		return super.listByPage(detachCriteria, page);
	}

	/**
	 * 查询流程结束或则审核通过的实体
	 * 
	 * @param detachCriteria
	 * @param page
	 * @return
	 */
	public PaginationSupport pageQueryValid(
			final DetachedCriteria detachCriteria, final PaginationSupport page) {
		detachCriteria.add(Restrictions.or(Restrictions.eq("flowstatus",
				Constrains.WORKFLOWENTITY_STATE_VALID), Restrictions.eq(
				"flowstatus", Constrains.WORKFLOWENTITY_STATE_NOT)));
		return super.listByPage(detachCriteria, page);
	}

	/**
	 * 查询在审核流程中的实体
	 * 
	 * @param detachCriteria
	 * @param page
	 * @return
	 */
	public PaginationSupport pageQueryInProcess(
			final DetachedCriteria detachCriteria, final PaginationSupport page) {
		detachCriteria.add(Restrictions.eq("flowstatus",
				Constrains.WORKFLOWENTITY_STATE_PROCESS));
		return super.listByPage(detachCriteria, page);
	}

	public void onProcessEnd(WorkFlowProcess process) {
		//System.out.println(process + "  process");
		T entity = getProcessEntity(process);
		if (process.getResult() == Constrains.WORKFLOWPROCESS_RESULT_PASS) {
			//System.out.println(entity + "               sadfg");
			entity.setFlowstatus(Constrains.WORKFLOWENTITY_STATE_VALID);
		} else {
			entity.setFlowstatus(Constrains.WORKFLOWENTITY_STATE_INVALID);
		}
		this.getWorkflowProcessService().saveProcessEntity(entity);
	}

	@SuppressWarnings("unchecked")
	public T getProcessEntity(WorkFlowProcess process) {

		return (T) this.getWorkflowProcessService().findProcessEntity(process,
				getFormClass());
	}

	@SuppressWarnings("unchecked")
	public PaginationSupport listFlowTask(Class cls, PaginationSupport page,
			Person person) {
		page = this.getWorkflowProcessService().pageQueryFlowNode(cls, page,
				person,
				new Integer[] { Constrains.WORKFLOWPROCESS_STATE_PROCESS });
		if (page.getItems() != null) {
			List<WorkFlowNodeVo> volist = new ArrayList<WorkFlowNodeVo>();
			for (WorkFlowProcessNode node : (Collection<WorkFlowProcessNode>) page
					.getItems()) {
				WorkFlowNodeVo vo = new WorkFlowNodeVo();
				vo.setEntity(this.getWorkflowProcessService()
						.findProcessEntity(node.getFlowProcess(), cls));
				vo.setNode(node);
				volist.add(vo);
			}
			page.setItems(volist);
		}
		return page;
	}

	public boolean inWorkFlowProcess(T entity) {
		if (entity != null
				&& entity.getProcess() != null
				&& entity.getProcess().getStatus() == Constrains.WORKFLOWPROCESS_STATE_PROCESS)
			return true;
		else
			return false;
	}
}

